﻿using Net.Event;
using System;
using System.Collections.Concurrent;
using System.Threading;

namespace Net.System
{
    public abstract class WorkerItemBase
    {
        public abstract void Invoke();
    }

    public class WorkerItem1 : WorkerItemBase
    {
        public readonly Action callback;

        public WorkerItem1(Action callback)
        {
            this.callback = callback;
        }

        public override void Invoke()
        {
            callback();
        }
    }

    public class WorkerItem2 : WorkerItemBase
    {
        public readonly WaitCallback callback;
        public readonly object state;

        public WorkerItem2(WaitCallback callback, object state)
        {
            this.callback = callback;
            this.state = state;
        }

        public override void Invoke()
        {
            callback(state);
        }
    }

    public class WorkerItemT<T> : WorkerItemBase
    {
        public readonly Action<T> callback;
        public readonly T state;

        public WorkerItemT(Action<T> callback, T state)
        {
            this.callback = callback;
            this.state = state;
        }

        public override void Invoke()
        {
            callback(state);
        }
    }

    public class ThreadWorker
    {
        private Thread thread;
        private readonly ConcurrentQueue<WorkerItemBase> workerQueue = new();
        private readonly AutoResetEvent workerAvailable = new(false);
        private bool isRunning;
        public Action<Exception> OnException { get; set; }

        public void Start(string name)
        {
            isRunning = true;
            thread = new Thread(DoWorker)
            {
                Name = name,
                IsBackground = true
            };
            thread.Start();
        }

        private void DoWorker()
        {
            try
            {
                while (isRunning)
                {
                    workerAvailable.WaitOne();
                    while (workerQueue.TryDequeue(out var workerItem))
                    {
                        workerItem.Invoke();
                    }
                }
            }
            catch (Exception ex)
            {
                if (OnException == null)
                    NDebug.LogError("线程池异常:" + ex);
                else
                    OnException(ex);
            }
        }

        public void QueueWorker(Action callback)
        {
            workerQueue.Enqueue(new WorkerItem1(callback));
            workerAvailable.Set();
        }

        public void QueueWorker(WaitCallback callback, object state)
        {
            workerQueue.Enqueue(new WorkerItem2(callback, state));
            workerAvailable.Set();
        }

        public void QueueWorker<T>(Action<T> callback, T state)
        {
            workerQueue.Enqueue(new WorkerItemT<T>(callback, state));
            workerAvailable.Set();
        }

        public void Stop()
        {
            isRunning = false;
            workerAvailable.Set();
            thread.Interrupt();
        }

        public override string ToString()
        {
            return $"Name:{thread.Name} Queue:{workerQueue.Count}";
        }
    }
}